package xing.appwidget.label

import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.DialogFragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.BiFunction
import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.kotlin.toFlowable
import io.reactivex.rxjava3.schedulers.Schedulers
import xing.appwidget.R
import xing.appwidget.bean.ApplicationItem
import xing.appwidget.bean.PackageFilterParam
import xing.appwidget.databinding.LayoutCreateLabelBinding
import xing.appwidget.storage.AppInfoStorageHelper
import xing.appwidget.storage.LabelStorageHelper
import xing.appwidget.widget.AppFilterView
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet

class LabelDetailFragment(private val labelName: String?, private val editMode: Boolean) : DialogFragment(), LabelManagerFragment.OnLabelSelectedListener {

    companion object {
        fun start(activity: AppCompatActivity, labelName: String?, isEditMode: Boolean) {
            LabelDetailFragment(labelName, isEditMode)
                    .show(activity.supportFragmentManager, LabelDetailFragment::class.java.simpleName)
        }
    }

    val disposeList = ArrayList<Disposable>()

    private lateinit var binding: LayoutCreateLabelBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(STYLE_NORMAL, R.style.Dialog_FullScreen)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = LayoutCreateLabelBinding.inflate(inflater, container, false)
        binding.root.setOnClickListener { dismiss() }
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        with(binding.etLabelName) {
            binding.ivDone.isEnabled = false
            if (!labelName.isNullOrBlank()) {
                setText(labelName)
            }
            if (editMode) {
                addTextChangedListener {
                    binding.ivDone.isEnabled = !editMode || text.isNotBlank()
                }
            }
        }
        with(binding.appFilter) {
            if (editMode) {
                visibility = View.VISIBLE
                setAppListView(binding.appList)
                setDataProvider(object : AppFilterView.DataProvider {
                    override fun request(param: PackageFilterParam?) {
                        getData(false, param)
                    }

                    override fun openLabelList(param: PackageFilterParam?) {
                        LabelManagerFragment.start(activity as AppCompatActivity, false, this@LabelDetailFragment, getLabels())
                    }
                })
                val param = PackageFilterParam(system = false, initWithGrid = false)
                if (!labelName.isNullOrEmpty()) {
                    param.labels = Collections.singleton(labelName)
                }
                getData(true, param)
                setParam(param)
            } else {
                visibility = View.GONE
                val pm = context?.packageManager
                if (pm != null && labelName != null) {
                    val dispose = LabelStorageHelper.getAppInfoListByLabel(pm, labelName)
                            .subscribeBy(onError = { e -> Log.e("LabelDetailFragment", "packageNames2AppInfos", e) },
                                    onSuccess = { appInfo -> binding.appList.setData(appInfo) })
                    disposeList.add(dispose)
                }
            }
        }
        binding.appList.setEdieMode(editMode)
        binding.ivDone.setOnClickListener {
            if (binding.ivDone.isEnabled) {
                if (editMode) {
                    val labelNameInput = binding.etLabelName.text.toString()

                    if (!labelName.isNullOrEmpty() && labelName != labelNameInput) {
                        LabelStorageHelper.delLabel(labelName)
                    }

                    val selectedPackage = binding.appList.getSelectedPackageName()
                    LabelStorageHelper.saveLabelSetting(labelNameInput, selectedPackage)
                }
                dismiss()
            }
        }
        binding.tvSelectAll.setOnClickListener { binding.appList.selectAll() }
        binding.tvUnSelectAll.setOnClickListener { binding.appList.unSelectAll() }
    }

    private fun getData(isInit: Boolean, param: PackageFilterParam?) {
        val context = context
        if (param == null || context == null) {
            return
        }
        val single1: Single<List<ApplicationItem>> = AppInfoStorageHelper
                .getAppInfoWithFilter(context.packageManager, param)
        val tempLabelList = HashSet<String>()
        if (!labelName.isNullOrEmpty()) {
            tempLabelList.add(labelName)
        }
        val single2: Single<List<Set<String>>> =
                if (!isInit) Single.just(ArrayList(0))
                else tempLabelList.toFlowable()
                        .map { label -> LabelStorageHelper.getPackageNameListByLabel(label) }
                        .toList()
        val biFunction = BiFunction<List<ApplicationItem>, List<Set<String>>, Unit> { t1, t2 ->
            if (t2.isNotEmpty()) {
                val set = HashSet<String>()
                t2.forEach { set.addAll(it) }
                binding.appList.setSelectedPacakgeByName(set)
            }
            binding.appList.setData(t1)
        }
        val disposable = Single.zip(single1, single2, biFunction)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe()
        disposeList.add(disposable)
    }

    override fun onDestroy() {
        doDispose()
        super.onDestroy()
    }

    override fun dismiss() {
        doDispose()
        super.dismissAllowingStateLoss()
    }

    private fun doDispose() {
        for (disposable in disposeList) {
            if (!disposable.isDisposed) {
                disposable.dispose()
            }
        }
        disposeList.clear()
    }

    override fun onSelected(labels: Collection<String>) {
        binding.appFilter.setLabels(labels)
    }

}